/*
 *  FILE: vfs_syscall.c
 *  AUTH: mcc | jal
 *  DESC:
 *  DATE: Wed Apr  8 02:46:19 1998
 *  $Id: vfs_syscall.c,v 1.1 2012/10/10 20:06:46 william Exp $
 */

#include "kernel.h"
#include "errno.h"
#include "globals.h"
#include "fs/vfs.h"
#include "fs/file.h"
#include "fs/vnode.h"
#include "fs/vfs_syscall.h"
#include "fs/open.h"
#include "fs/fcntl.h"
#include "fs/lseek.h"
#include "mm/kmalloc.h"
#include "util/string.h"
#include "util/printf.h"
#include "fs/stat.h"
#include "util/debug.h"


/* To read a file:
 *      o fget(fd) (check)
 *      o call its virtual read f_op (check)
 *      o update f_pos (check)
 *      o fput() it (check)
 *      o return the number of bytes read, or an error (check)
 *
 * Error cases you must handle for this function at the VFS level:
 *      o EBADF (check)
 *        fd is not a valid file descriptor or is not open for reading.
 *      o EISDIR (check)
 *        fd refers to a directory.
 *
 * In all cases, be sure you do not leak file refcounts by returning before (check)
 * you fput() a file that you fget()'ed.
 */
int
do_read(int fd, void *buf, size_t nbytes)
{
	dbgq(DBG_SCHED,"TRYING TO READ on fd %d\n",fd);
	int result,status;	
	file_t *f;
	if(fd<0 || fd>=NFILES)
	{
		dbgq(DBG_SCHED,"ERROR FOUND while reading on file. Error number is  %d \n",-EBADF);				
				
		return -EBADF;
	}	
	if(!curproc->p_files[fd])
	{	
		dbgq(DBG_SCHED,"ERROR FOUND while reading on file. Error number is  %d \n",-EBADF);				
				
		return -EBADF;
	}
	f= fget(fd);	
	if(f)
	{
		if(S_ISDIR(f->f_vnode->vn_mode))
		{
			dbgq(DBG_SCHED,"ERROR FOUND reading reading on file. Error number is  %d \n",-EISDIR);				
			fput(f);			
			return -EISDIR;
		}		
		if(f->f_mode & FMODE_READ)
		{		
			result = f->f_vnode->vn_ops->read(f->f_vnode, f->f_pos, buf, nbytes);
			if(result>=0)		
			{
				f->f_pos+=result;		
			}
			fput(f);
			dbgq(DBG_SCHED,"SUCCESSFUL READ on file\n");			
			return result;
		}
		else
		{
			fput(f);
			dbgq(DBG_SCHED,"ERROR FOUND while reading on file. Error number is  %d \n",-EBADF);				
		
			return -EBADF;
		}
	}
	else
	{
		dbgq(DBG_SCHED,"ERROR FOUND while reading on file. Error number is  %d \n",-EBADF);				
				
		return -EBADF;
        }
	/*NOT_YET_IMPLEMENTED("VFS: do_read");
        return -1;*/
}

/* Very similar to do_read.  Check f_mode to be sure the file is writable.  If (check all)
 * f_mode & FMODE_APPEND, do_lseek() to the end of the file, call the write
 * f_op, and fput the file.  As always, be mindful of refcount leaks.
 *
 * Error cases you must handle for this function at the VFS level:
 *      o EBADF (check)
 *        fd is not a valid file descriptor or is not open for writing.
 */
int
do_write(int fd, const void *buf, size_t nbytes)
{
	dbgq(DBG_SCHED,"TRYING TO WRITE on fd %d \n",fd);				
				
	int result,status;	
	file_t *f;
	if(fd<0 || fd>=NFILES)
	{
		dbgq(DBG_SCHED,"ERROR FOUND while writing on file. Error number is  %d \n",-EBADF);				
				
		return -EBADF;
	}
	if(!curproc->p_files[fd])
	{	
	dbgq(DBG_SCHED,"ERROR FOUND while writing on file. Error number is  %d \n",-EBADF);				
				
		return -EBADF;
	}	
	f= fget(fd);	
	if(f)
	{
		if(f->f_mode & FMODE_WRITE)
		{		
			if(f->f_mode & FMODE_APPEND)
			{			
				status = do_lseek(fd,0,SEEK_END); 
				if(status<0)
				{
					fput(f);			
					return status;
				}
			}
			result = f->f_vnode->vn_ops->write(f->f_vnode, f->f_pos, buf, nbytes);
			if(result>=0)		
			{
				KASSERT((S_ISCHR(f->f_vnode->vn_mode)) ||
                                (S_ISBLK(f->f_vnode->vn_mode)) ||
                                ((S_ISREG(f->f_vnode->vn_mode)) && (f->f_pos <= f->f_vnode->vn_len)));

				f->f_pos+=result;
			}
			fput(f);
			dbgq(DBG_SCHED,"SUCCESSFULL WRITE ON FILE\n");				
			return result;
		}
		else
		{
			dbgq(DBG_SCHED,"ERROR FOUND while writing on file. Error number is  %d \n",-EBADF);				
					
			fput(f);
			return -EBADF;
		}
	}
	else
	{
		dbgq(DBG_SCHED,"ERROR FOUND while writing on file. Error number is  %d \n",-EBADF);				
				
		return -EBADF;
	}
/*
        NOT_YET_IMPLEMENTED("VFS: do_write");
        return -1;*/
}

/*
 * Zero curproc->p_files[fd], and fput() the file. Return 0 on success (check)
 *
 * Error cases you must handle for this function at the VFS level:
 *      o EBADF (check)
 *        fd isn't a valid open file descriptor.
 */
int
do_close(int fd)
{
	file_t *f;
	if(fd<0 || fd>=NFILES)
	{
		dbgq(DBG_SCHED,"fd<0 or fd> 28 error. Error No.  %d \n",-EBADF);		
		return -EBADF;
	}	
	if(!curproc->p_files[fd])
	{	
		return -EBADF; 
	}
	f = curproc->p_files[fd];

	f= fget(fd);	

	if(f)
	{
		curproc->p_files[fd] = NULL;
                fput(f);
		fput(f);	
		return 0;
	}
	else
	{
		dbgq(DBG_SCHED,"ERROR FOUND while closing on file. Error number is  %d \n",-EBADF);		
		return -EBADF;
	}
	/*NOT_YET_IMPLEMENTED("VFS: do_close");
        return -1; */
}

/* To dup a file:
 *      o fget(fd) to up fd's refcount (check)
 *      o get_empty_fd() (check)
 *      o point the new fd to the same file_t* as the given fd (check)
 *      o return the new file descriptor (check)
 *
 * Don't fput() the fd unless something goes wrong.  Since we are creating
 * another reference to the file_t*, we want to up the refcount.
 *
 * Error cases you must handle for this function at the VFS level:
 *      o EBADF (check)
 *        fd isn't an open file descriptor.
 *      o EMFILE (check)
 *        The process already has the maximum number of file descriptors open
 *        and tried to open a new one.
 */
int
do_dup(int fd)
{
	file_t *f;
	int new_fd;
	if(fd<0 || fd>=NFILES)
	{
		dbgq(DBG_SCHED,"fd<0 or fd> NFILES  %d \n",-EBADF);		
		return -EBADF;
	}
	if(!curproc->p_files[fd])
	{	
		dbgq(DBG_SCHED,"fd isn't a open file descriptor  %d \n",-EBADF);		
		return -EBADF;
	}
	f = fget(fd);
	if(f)
	{
		new_fd = get_empty_fd(curproc);
		if(new_fd!=EMFILE)
		{
			curproc->p_files[new_fd] = f;
			dbgq(DBG_SCHED,"SUCCESSFULL dup. NEW FILE DESCRIPTOR is   %d . PREVIOUS file descriptor was %d\n",new_fd,fd);
			return new_fd;
		}
		else
		{
			fput(f);
			dbgq(DBG_SCHED,"The process already has the maximum number of file descriptors open and tried to open a new one  %d \n",-EBADF);
			return -EMFILE;
		}
	}
	else
	{
		dbgq(DBG_SCHED,"fd isn't a open file descriptor  %d \n",-EBADF);		
		return -EBADF;  		      
	}
	/*NOT_YET_IMPLEMENTED("VFS: do_dup");
        return -1;*/
}

/* Same as do_dup, but insted of using get_empty_fd() to get the new fd, (check)
 * they give it to us in 'nfd'.  If nfd is in use (and not the same as ofd)
 * do_close() it first.  Then return the new file descriptor.
 *
 * Error cases you must handle for this function at the VFS level:
 *      o EBADF (check)
 *        ofd isn't an open file descriptor, or nfd is out of the allowed
 *        range for file descriptors.
 */
int
do_dup2(int ofd, int nfd)
{
	file_t *f;
	int new_fd;
	if(ofd<0 || ofd>=NFILES)
	{
		dbgq(DBG_SCHED,"fd for dup2 isn't a open file descriptor.ERROR NO.  %d \n",-EBADF);		
		return -EBADF;
	}	
	if(!curproc->p_files[ofd] || (nfd < 0 || nfd >= NFILES))
	{	
		dbgq(DBG_SCHED,"fd for dup2 is out of range> ERROR NO.  %d \n",-EBADF);		
		return -EBADF;
	}	
	f = fget(ofd);
	if(f)
	{
		if(curproc->p_files[nfd] && ofd!=nfd)
		{
			if(!(do_close(nfd)))
			{
				fput(f);				
				return -EBADF;
			}
		}
		new_fd = nfd;
		curproc->p_files[new_fd] = f;
		dbgq(DBG_SCHED,"SUCCESSFULL dup2. NEW FILE DESCRIPTOR is   %d . PREVIOUS file descriptor was %d\n",new_fd,ofd);
		return new_fd;
	}
	else
	{
		dbgq(DBG_SCHED,"ERROR IN dup2. ERROR NO.  %d \n",-EBADF);		
		return -EBADF;  		      
	}
        /*NOT_YET_IMPLEMENTED("VFS: do_dup2");
        return -1;*/
}


/*
 * This routine creates a special file of the type specified by 'mode' at
 * the location specified by 'path'. 'mode' should be one of S_IFCHR or
 * S_IFBLK (you might note that mknod(2) normally allows one to create
 * regular files as well-- for simplicity this is not the case in Weenix).
 * 'devid', as you might expect, is the device identifier of the device
 * that the new special file should represent.
 *
 * You might use a combination of dir_namev, lookup, and the fs-specific
 * mknod (that is, the containing directory's 'mknod' vnode operation).
 * Return the result of the fs-specific mknod, or an error.
 *
 * Error cases you must handle for this function at the VFS level:
 *      o EINVAL
 *        mode requested creation of something other than a device special
 *        file.
 *      o EEXIST
 *        path already exists.
 *      o ENOENT
 *        A directory component in path does not exist.
 *      o ENOTDIR
 *        A component used as a directory in path is not, in fact, a directory.
 *      o ENAMETOOLONG
 *        A component of path was too long.
 */
/*
 * Author : Vishwanath Eswarakrishnan 
*/

int
do_mknod(const char *path, int mode, unsigned devid)
{
	      
	dbgq(DBG_SCHED,"TRYING TO MAKE SPECIAL FILE. PATH IS   %s and mode is %d \n",path,mode);	
		
	/* NOT_YET_IMPLEMENTED("VFS: do_mknod");*/
	vnode_t *nvnode;
	vnode_t *altvnode;
	
	const char *name;
	size_t namelen;
	
	if(strcmp(path,"")==0)
	{
		dbgq(DBG_SCHED,"ERROR FOUND while creating Special File. Error number is  %d \n",-EINVAL);		
		return -EINVAL;
	}

	if(S_ISCHR(mode) || S_ISBLK(mode))
	{
		
		
		int retDir=dir_namev(path,&namelen,&name,NULL,&nvnode);
		if(retDir>=0)
		{ vput(nvnode); }
		if(retDir==-ENOENT)
		{
			dbgq(DBG_SCHED,"ERROR FOUND while creating Special File. Error number is  %d \n",-ENOENT);			
			return -ENOENT;	
		}
		if(strlen(name)>NAME_LEN) 
		{
			if(retDir>=0)			
			{ vput(nvnode); }
			dbgq(DBG_SCHED,"ERROR FOUND while creating Special File. Error number is  %d \n",-ENAMETOOLONG);
			return -ENAMETOOLONG;
		}
		
		int lookupResult=lookup(nvnode,name,namelen,&altvnode);
		if(lookupResult==-EINVAL)
		{
			dbgq(DBG_SCHED,"ERROR FOUND while creating Special File. Error number is  %d \n",-EINVAL);			
			return -EINVAL;
		}		
		if(lookupResult==-ENOTDIR)	
		{
			dbgq(DBG_SCHED,"ERROR FOUND while creating Special File. Error number is  %d \n",-ENOTDIR);			
			return -ENOTDIR;	
		}
		if(lookupResult==0)
		{					
			dbgq(DBG_SCHED,"ERROR FOUND while creating Special File. Error number is  %d \n",-EEXIST);		  	
			vput(altvnode);      
			return -EEXIST;
		}	

		KASSERT(NULL!=nvnode->vn_ops->mknod);
             
		int toRet=nvnode->vn_ops->mknod(nvnode,name,namelen,mode,devid);
		int z=lookup(nvnode, name, namelen, &altvnode); 
		dbgq(DBG_SCHED,"SUCCESSFULLY CREATED SPECIAL FILE of path %s and mode %d\n",path,mode);
		/* MKMOD(2)	*/
		return toRet;				
	}
	else
	{
		dbgq(DBG_SCHED,"ERROR FOUND while creating Special File. Error number is  %d \n",-EINVAL);		
		return -EINVAL;
	}
		
		
        return -1;
}
/* Use dir_namev() to find the vnode of the dir we want to make the new
 * directory in.  Then use lookup() to make sure it doesn't already exist.
 * Finally call the dir's mkdir vn_ops. Return what it returns.
 *
 * Error cases you must handle for this function at the VFS level:
 *      o EEXIST
 *        path already exists.
 *      o ENOENT
 *        A directory component in path does not exist.
 *      o ENOTDIR
 *        A component used as a directory in path is not, in fact, a directory.
 *      o ENAMETOOLONG
 *        A component of path was too long.
 */

/* Author : Aditya Jain
Description:
Used dir_namev to get the vnode of the dir. Checked if res_vnode not NULL. 
Used lookup to check if path exists or not. Then used mkdir to make directory
*/

int
do_mkdir(const char *path)
{
        
	dbgq(DBG_SCHED,"TRYING TO MAKE DIRECTORY. PATH IS   %s \n",path);	
	if(strcmp(path,"")==0)
	{
		dbgq(DBG_SCHED,"ERROR FOUND while creating Directory. Error number is  %d \n",-EINVAL);		
		return -EINVAL;
	}	
	if(strlen(path)>MAXPATHLEN)
	{
		dbgq(DBG_SCHED,"ERROR FOUND while creating Directory. Error number is  %d \n",-ENAMETOOLONG);		
		return -ENAMETOOLONG;
	}
	
	char *teststr=(char*)kmalloc(sizeof(char*)*1025);
	strcpy(teststr, path);
	char * pch;
  	pch = strtok (teststr,"/");
  	
	while (pch != NULL)
        {
		if(strlen(pch)>NAME_LEN)
		{
			dbgq(DBG_SCHED,"ERROR FOUND while creating Directory. Error number is  %d \n",-ENAMETOOLONG);			
			return -ENAMETOOLONG;           
		}		
		pch = strtok (NULL, "/");
        }
         
        size_t namelen=0;
	const char *name=NULL;
	vnode_t *res_vnode=NULL;
	vnode_t *result=NULL;
	dbgq(DBG_SCHED,"---1\n");	
	if (strtok(teststr,"/")==NULL)
	{
		dbgq(DBG_SCHED,"/ found \n");		
			return -EEXIST;
	}
	kfree(teststr);
	
	int x=dir_namev(path, &namelen, &name, NULL, &res_vnode);
	dbgq(DBG_SCHED,"---2\n");
	if(x==-ENOENT)
	{
		
		if(res_vnode!=NULL)
		{	
			int m=lookup(res_vnode,path,strlen(path),&result);	
			if(m>=0)
			{
				vput(result);
				return -EEXIST;
			}
		}	
		dbgq(DBG_SCHED,"ERROR FOUND while creating Directory %s. Error number is  %d \n",path,-ENOENT);		
		return -ENOENT;
	}
	dbgq(DBG_SCHED,"---3\n");	
	if(namelen>NAME_LEN || x==-ENAMETOOLONG)
	{
		if(x>=0)
		{
			vput(res_vnode);     		
		}
		dbgq(DBG_SCHED,"ERROR FOUND while creating Directory. Error number is  %d \n",-ENAMETOOLONG);
		return -ENAMETOOLONG;
	} 
	dbgq(DBG_SCHED,"---4\n");
	if(res_vnode!=NULL)
	{
		if (!S_ISDIR(res_vnode->vn_mode))
		{
			if(x>=0)
			{
				vput(res_vnode);     
			}
			dbgq(DBG_SCHED,"ERROR FOUND while creating Directory. Error number is  %d \n",-ENOTDIR);		
			return -ENOTDIR;
		}
	}
	dbgq(DBG_SCHED,"---5\n");
	if(x<0)
		return x;
	if(x>=0)
	{
		vput(res_vnode);     
	}		
	x=lookup(res_vnode, name, namelen, &result);
	dbgq(DBG_SCHED,"---6\n");
	if (x==-ENOTDIR || x==-ENOENT || x<0)
	{
		KASSERT(NULL !=res_vnode->vn_ops->mkdir);		
		int k=res_vnode->vn_ops->mkdir(res_vnode,name,namelen);    	
		int z=lookup(res_vnode, name, namelen, &result);	
		dbgq(DBG_SCHED,"DIRECTORY SUCCESSFULLY CREATED. PATH IS %s\n",path);			
		return k;		
	}
	else
	{				
		dbgq(DBG_SCHED,"ERROR FOUND while creating Directory. Error number is  %d \n",-EEXIST);		
		vput(result);     
		return -EEXIST;	
	}
	/*NOT_YET_IMPLEMENTED("VFS: do_mkdir"); */
       
}

/* Use dir_namev() to find the vnode of the directory containing the dir to be
 * removed. Then call the containing dir's rmdir v_op.  The rmdir v_op will
 * return an error if the dir to be removed does not exist or is not empty, so
 * you don't need to worry about that here. Return the value of the v_op,
 * or an error.
 *
 * Error cases you must handle for this function at the VFS level:
 *      o EINVAL
 *        path has "." as its final component.
 *      o ENOTEMPTY
 *        path has ".." as its final component.
 *      o ENOENT
 *        A directory component in path does not exist.
 *      o ENOTDIR
 *        A component used as a directory in path is not, in fact, a directory.
 *      o ENAMETOOLONG
 *        A component of path was too long.
 */

/* Author : Aditya Jain
Error casses EINVAL,ENOTEMPTY,ENAMETOOLONG LEFT 
NAME SHOULD NOT BE LONGER THAN NAME_LEN
*/

int
do_rmdir(const char *path)
{
 	dbgq(DBG_SCHED,"TRYING TO REMOVE DIRECTORY. PATH IS   %s \n",path);
	if(strcmp(path,"")==0)
	{
		dbgq(DBG_SCHED,"ERROR FOUND while removing Directory. Error number is  %d \n",-EINVAL);		
		return -EINVAL;
	}       
	
	if(strlen(path)>MAXPATHLEN)
	{
		dbgq(DBG_SCHED,"ERROR FOUND while removing Directory. Error number is  %d \n",-ENAMETOOLONG);		
		return -ENAMETOOLONG;
	}
	char *teststr=(char*)kmalloc(sizeof(char*)*1025);
	strcpy(teststr, path);
	char * pch;
  	pch = strtok (teststr,"/");
  	
	while (pch != NULL)
        {
		if(strlen(pch)>NAME_LEN)
		{
			dbgq(DBG_SCHED,"ERROR FOUND while removing Directory. Error number is  %d \n",-ENAMETOOLONG);			
			return -ENAMETOOLONG;           
		}		
		pch = strtok (NULL, "/");
        }
         
        
	if (strtok(teststr,"/")==NULL)
	{
		dbgq(DBG_SCHED,"ERROR FOUND while removing Directory. Error number is  %d \n",-ENOENT);			
		return -ENOENT;
	}

        kfree(teststr);
	size_t namelen=0;
	const char *name=NULL;
	vnode_t *res_vnode=NULL;
	int x=dir_namev(path, &namelen, &name, NULL, &res_vnode);
	
	
	if(namelen>NAME_LEN || x==-ENAMETOOLONG)
	{
		if(x>=0)
		{
			vput(res_vnode);     		
		}
		dbgq(DBG_SCHED,"ERROR FOUND while removing Directory. Error number is  %d \n",-ENAMETOOLONG);	
		return -ENAMETOOLONG;
	}
	
	
	
	if(x<0)
	{
		dbgq(DBG_SCHED,"ERROR FOUND while removing Directory. Error number is  %d \n",-ENOENT);		
		return x;
	}
	
	/*if (!S_ISDIR(res_vnode->vn_mode))
	{
		if(x>=0)
		{
			vput(res_vnode);     
		}	
		dbgq(DBG_SCHED,"ERROR FOUND while removing Directory. Error number is  %d \n",-ENOTDIR);		
		return -ENOTDIR;
	} */

	if(name_match(".",name,namelen))
	{
		if(x>=0)
		{
			vput(res_vnode);     
		}
		dbgq(DBG_SCHED,"ERROR FOUND while removing Directory. Error number is  %d \n",-EINVAL);					
		return -EINVAL;
	}

	if(name_match("..", name,namelen))
	{
		if(x>=0)
		{
			vput(res_vnode);     
		}	
		dbgq(DBG_SCHED,"ERROR FOUND while removing Directory. Error number is  %d \n",-ENOTEMPTY);			
		return -ENOTEMPTY;
	}
	if(x<0)
	{
		dbgq(DBG_SCHED,"ERROR FOUND while removing Directory. Error number is  %d \n",x);				
		return x;
	}	
	else
		{vput(res_vnode);     }
	
	vnode_t * result;	
	x=lookup(res_vnode, name, namelen, &result);
	
	if(x<0)
	{
		dbgq(DBG_SCHED,"ERROR FOUND while removing Directory. Error number is  %d \n",x);				
		return x;
	}
	
	if (!S_ISDIR(result->vn_mode))
	{
		if(x>=0)
		{
			vput(result);     
		}	
		dbgq(DBG_SCHED,"ERROR FOUND while removing Directory. Error number is  %d \n",-ENOTDIR);		
		return -ENOTDIR;
	}
	vput(result);     
	KASSERT(NULL !=res_vnode->vn_ops->rmdir);
	int k= res_vnode->vn_ops->rmdir(res_vnode,name,namelen);
	
	if(k>=0)	
		{vput(result);     }
	dbgq(DBG_SCHED,"SUCCESSFULLY REMOVED DIRECTORY OF PATH  %s \n",path);		
	return k;
	
	/* NOT_YET_IMPLEMENTED("VFS: do_rmdir"); */
       
}

/*
 * Same as do_rmdir, but for files.
 *
 * Error cases you must handle for this function at the VFS level:
 *      o EISDIR
 *        path refers to a directory.
 *      o ENOENT
 *        A component in path does not exist.
 *      o ENOTDIR
 *        A component used as a directory in path is not, in fact, a directory.
 *      o ENAMETOOLONG
 *        A component of path was too long.
 */
int
do_unlink(const char *path)
{
        
	dbgq(DBG_SCHED,"Trying to unlink path %s \n",path);
	if(strcmp(path,"")==0)
	{
		dbgq(DBG_SCHED,"Error in unlinking.String Empty.Error No. %d \n",-EINVAL);		
		return -EINVAL;
	}
	if(strlen(path)>MAXPATHLEN)
	{
		dbgq(DBG_SCHED,"Error in unlinking.String too long.Error No. %d \n",-ENAMETOOLONG);		
		return -ENAMETOOLONG;
	}
	char *teststr=(char*)kmalloc(sizeof(char*)*1025);
	strcpy(teststr, path);
	char * pch;
  	pch = strtok (teststr,"/");
  	
	while (pch != NULL)
        {
		if(strlen(pch)>NAME_LEN)
		{
			dbgq(DBG_SCHED,"Error in unlinking.String too long.Error No. %d \n",-ENAMETOOLONG);			
			return -ENAMETOOLONG;           
		}		
		pch = strtok (NULL, "/");
        }
         
	
	if (strtok(teststr,"/")==NULL)
	{
		return -ENOENT;
	}

        kfree(teststr);
        size_t namelen=0;
	const char *name=NULL;
	vnode_t *res_vnode=NULL;
	int x=dir_namev(path, &namelen, &name, NULL, &res_vnode);
	
	if(res_vnode==NULL || x==-ENOTDIR)
	{
		dbgq(DBG_SCHED,"Error in unlinking..Error No. %d \n",-ENOENT);		
			
		return -ENOENT;
	}
	if (!S_ISDIR(res_vnode->vn_mode))
	{
		if(x>=0)
			{vput(res_vnode);     }	
		dbgq(DBG_SCHED,"Error in unlinking..Error No. %d \n",-ENOTDIR);		
		
		return -ENOTDIR;
	}

	
	if(namelen>NAME_LEN || x==-ENAMETOOLONG)
	{
		if(x>=0)
			{vput(res_vnode);     }		
		return -ENAMETOOLONG;
	}
	
	if(x<0)
	{
		dbgq(DBG_SCHED,"Error in unlinking.Error No. %d \n",x);		
			
		return x;
	}	
	else
	{vput(res_vnode);     }
	
	vnode_t * result;	
	x=lookup(res_vnode, name, namelen, &result);
	if(x>=0)
		vput(result);
	if(x<0)
	{
		dbgq(DBG_SCHED,"Error in unlinking.Error No. %d \n",x);		
			
		return x;
	}	
	if (S_ISDIR(result->vn_mode))
	{		
		dbgq(DBG_SCHED,"Error in unlinking.INPUT is a directory.Error No. %d \n",-EISDIR);				
		return -EISDIR;
	}
	 KASSERT(NULL != res_vnode->vn_ops->unlink);
	int k= res_vnode->vn_ops->unlink(res_vnode,name,namelen);
	if(k>=0)	
		{vput(result);     }
	dbgq(DBG_SCHED,"SUCCESS IN UNLINKING\n");		
	
	return k;
	
	
	/* NOT_YET_IMPLEMENTED("VFS: do_unlink"); */
}

/* To link:
 *      o open_namev(from)
 *      o dir_namev(to)
 *      o call the destination dir's (to) link vn_ops.
 *      o return the result of link, or an error
 *
 * Remember to vput the vnodes returned from open_namev and dir_namev.
 *
 * Error cases you must handle for this function at the VFS level:
 *      o EEXIST
 *        to already exists.
 *      o ENOENT
 *        A directory component in from or to does not exist.
 *      o ENOTDIR
 *        A component used as a directory in from or to is not, in fact, a
 *        directory.
 *      o ENAMETOOLONG
 *        A component of from or to was too long.
 */
int
do_link(const char *from, const char *to)
{
	
	dbgq(DBG_SCHED,"Trying to Link from %s to %s \n",from,to);
	if(strcmp(from,"")==0 || strcmp(to,"")==0)
	{
		dbgq(DBG_SCHED,"ERROR in Linking.String Empty. Error number is  %d \n",-EINVAL);		
		return -EINVAL;
	}		
	if(strlen(from)>MAXPATHLEN || strlen(to)>MAXPATHLEN)
	{
		dbgq(DBG_SCHED,"ERROR in Linking.LENGTH IS TOO LONG. Error number is  %d \n",-ENAMETOOLONG);		
		return -ENAMETOOLONG;
	}
	char *teststr=(char*)kmalloc(sizeof(char*)*1025);
	strcpy(teststr, from);
	char * pch;
  	pch = strtok (teststr,"/");
  	
	while (pch != NULL)
        {
		if(strlen(pch)>NAME_LEN)
			return -ENAMETOOLONG;           
		pch = strtok (NULL, "/");
        }
        
	char *teststr1=(char*)kmalloc(sizeof(char*));
	strcpy(teststr1, to);
	char * pch1;
  	pch1 = strtok (teststr1,"/");
  	
	while (pch1 != NULL)
        {
		if(strlen(pch1)>NAME_LEN)
		{
			dbgq(DBG_SCHED,"ERROR in Linking.String too long. Error number is  %d \n",-ENAMETOOLONG);			
			return -ENAMETOOLONG;           
		}		
		pch1 = strtok (NULL, "/");
        }
         
        
     
	size_t namelen=0;
	const char *name=NULL;      	
	vnode_t *res_vnode_from=NULL;
	vnode_t *res_vnode_to=NULL;

	int x= open_namev(from,O_RDONLY, &res_vnode_from,NULL);
		
	
	if (strtok(teststr,"/")==NULL)
	{
		dbgq(DBG_SCHED,"ERROR in Linking. Error number is  %d \n",-ENOENT);		
		return -ENOENT;
	}
	
	if (strtok(teststr1,"/")==NULL)
	{
		dbgq(DBG_SCHED,"ERROR in Linking. Error number is  %d \n",-ENOENT);		
		return -ENOENT;
	}
        kfree(teststr);
	kfree(teststr1);
	
	if(namelen>NAME_LEN || x==-ENAMETOOLONG)
	{
		if(x>=0)
			{vput(res_vnode_from);     }	
		dbgq(DBG_SCHED,"ERROR in Linking.String too long. Error number is  %d \n",-ENAMETOOLONG);
		return -ENAMETOOLONG;
	}

	
	if(x<0)
	{
		dbgq(DBG_SCHED,"ERROR in Linking.. Error number is  %d \n",x);		
		return x;
	}
	else
		{vput(res_vnode_from);     }
	
	x=dir_namev(to, &namelen, &name, NULL, &res_vnode_to);
	vnode_t *result;	
	
	/*kfree(teststr); */ /* Giving error dont know why */
	
	if (res_vnode_to==NULL || x==-ENOENT)
	{
		if(x>=0)
			{vput(res_vnode_to);     }
		dbgq(DBG_SCHED,"ERROR in Linking.. Error number is  %d \n",-ENOENT);		
		return -ENOENT;
	} 

	if (!S_ISDIR(res_vnode_to->vn_mode))
	{
		if(x>=0)
			{vput(res_vnode_to);     }
		dbgq(DBG_SCHED,"ERROR in Linking.. Error number is  %d \n",-ENOTDIR);			
		return -ENOTDIR;
	}

	if(namelen>NAME_LEN || x==-ENAMETOOLONG)
	{
		if(x>=0)
			{vput(res_vnode_to);     }
		dbgq(DBG_SCHED,"ERROR in Linking..STRING TO LONG.. Error number is  %d \n",-ENAMETOOLONG);			
		return -ENAMETOOLONG;
	}
		
	vput(res_vnode_to);
	
	x=lookup(res_vnode_to, name, namelen, &result);
	if(x>=0)
	{
		vput(result);
		dbgq(DBG_SCHED,"ERROR in Linking..File EXISTS Error number is  %d \n",-EEXIST);	
		return -EEXIST;
	}	
	x=res_vnode_to->vn_ops->link(res_vnode_from, res_vnode_to, name, namelen);
	if(x>=0)
	{
			vref(res_vnode_from);     
	}	
	dbgq(DBG_SCHED,"SUCCESS in Linking.  \n");	
	return x;	

        /* NOT_YET_IMPLEMENTED("VFS: do_link"); */
        
}

/*      o link newname to oldname
 *      o unlink oldname
 *      o return the value of unlink, or an error
 *
 * Note that this does not provide the same behavior as the
 * Linux system call (if unlink fails then two links to the
 * file could exist).
 */
int
do_rename(const char *oldname, const char *newname)
{
        
	dbgq(DBG_SCHED,"Trying to rename  %s with new name %s\n",oldname,newname);	
	if(strcmp(oldname,"")==0 || strcmp(newname,"")==0)
	{
		dbgq(DBG_SCHED,"ERROR in renaming.Invalid args %d \n",-EINVAL);			
		return -EINVAL;
	}
	int x=do_link(oldname, newname);
	if (x<0)
	{
		dbgq(DBG_SCHED,"ERROR in renaming  %d \n",x);			
		return x;
	}
	dbgq(DBG_SCHED,"RENAMING SUCCESSFUL \n");	
	return do_unlink(oldname);

	/* NOT_YET_IMPLEMENTED("VFS: do_rename"); */
        
}

/* Make the named directory the current process's cwd (current working
 * directory).  Don't forget to down the refcount to the old cwd (vput()) and
 * up the refcount to the new cwd (open_namev() or vget()). Return 0 on
 * success.
 *
 * Error cases you must handle for this function at the VFS level:
 *      o ENOENT
 *        path does not exist.
 *      o ENAMETOOLONG
 *        A component of path was too long.
 *      o ENOTDIR
 *        A component of path is not a directory.
 */
int
do_chdir(const char *path)
{
       /* NOT_YET_IMPLEMENTED("VFS: do_chdir");*/
	dbgq(DBG_SCHED,"TRYING TO CHANGE DIRECTORY TO PATH %s\n",path);
	vnode_t *dirname;
	vnode_t *altdirname;
 	
	if(strcmp(path,"")==0)
	{
		dbgq(DBG_SCHED,"ERROR FOUND while changing Directory. Error number is  %d \n",-EINVAL);			
		return -EINVAL;
	}	
	
	vnode_t *olddir=curproc->p_cwd;
	
	const char *name;
	size_t namelen;
		
	int nameret=dir_namev(path,&namelen,&name,NULL,&dirname);
	dbgq(DBG_SCHED,"HERE 1 IN CD");			

	if(nameret>=0)
	{/*vput(dirname);	*/}

	if(nameret==-ENOENT)
	{
		dbgq(DBG_SCHED,"ERROR FOUND while changing Directory. Error number is  %d \n",-ENOENT);			
		return -ENOENT;		
	}	
	if(strlen(name)>NAME_LEN) 
	{
		dbgq(DBG_SCHED,"ERROR FOUND while changing Directory. Error number is  %d \n",-ENAMETOOLONG);			
		return -ENAMETOOLONG;
	}
	
	int lookupResult=lookup(dirname,name,namelen,&altdirname);
		dbgq(DBG_SCHED,"HERE 2 IN CD");
	if(lookupResult>=0)
		/*vput(altdirname);*/		
	
	if(lookupResult==-ENOENT)
	{
		dbgq(DBG_SCHED,"ERROR FOUND while changing Directory. Error number is  %d \n",-ENOENT);			
		return -ENOENT;
	}
	if(!S_ISDIR(altdirname->vn_mode))
	{
		dbgq(DBG_SCHED,"ERROR FOUND while changing Directory. Error number is  %d \n",-ENOTDIR);			
		return -ENOTDIR;
	}		
		
		
	if(lookupResult==-ENOTDIR)	
	{
		dbgq(DBG_SCHED,"ERROR FOUND while changing Directory. Error number is  %d \n",-ENOTDIR);			
		return -ENOTDIR;	
	}
		
	curproc->p_cwd=altdirname;
/*	vput(olddir);*/
	vref(altdirname);
	dbgq(DBG_SCHED,"SUCCESFULLY CHANGED DIRECTORY TO PATH %s\n",path);
	/*vput(dirname);*/
	dbgq(DBG_SCHED,"HERE 3 IN CD");	
        return 0;
}

/* Call the readdir f_op on the given fd, filling in the given dirent_t*.
 * If the readdir f_op is successful, it will return a positive value which
 * is the number of bytes copied to the dirent_t.  You need to increment the
 * file_t's f_pos by this amount.  As always, be aware of refcounts, check
 * the return value of the fget and the virtual function, and be sure the
 * virtual function exists (is not null) before calling it.
 *
 * Return either 0 or sizeof(dirent_t), or -errno.
 *
 * Error cases you must handle for this function at the VFS level:
 *      o EBADF
 *        Invalid file descriptor fd.
 *      o ENOTDIR
 *        File descriptor does not refer to a directory.
 */
int
do_getdent(int fd, struct dirent *dirp)
{
        /*NOT_YET_IMPLEMENTED("VFS: do_getdent");*/
	file_t *f;
	if(fd<0 || fd>=NFILES)
		return -EBADF;
	 	
	f=curproc->p_files[fd];
	
	if(f)
	{	
		if(!S_ISDIR(f->f_vnode->vn_mode))
			return -ENOTDIR;			
		
		int retRead=f->f_vnode->vn_ops->readdir(f->f_vnode,f->f_pos,dirp);	
		f->f_pos=f->f_pos+retRead;		
		if(retRead>0)
			return sizeof(dirent_t); 		
		return 0;
	}
	else
	{
		return -EBADF;
	}
}

/*
 * Modify f_pos according to offset and whence.
 *
 * Error cases you must handle for this function at the VFS level:
 *      o EBADF
 *        fd is not an open file descriptor.
 *      o EINVAL
 *        whence is not one of SEEK_SET, SEEK_CUR, SEEK_END; or the resulting
 *        file offset would be negative.
 */
int
do_lseek(int fd, int offset, int whence)
{
        /*NOT_YET_IMPLEMENTED("VFS: do_lseek");*/
	file_t *f=NULL;
	if(fd<0 || fd>=NFILES)
		return -EBADF;
	
	f=curproc->p_files[fd];
	
	if(fd<0 || f==NULL || S_ISCHR(f->f_vnode->vn_mode) || S_ISBLK(f->f_vnode->vn_mode))	
		return -EBADF;
	
	if(whence!=SEEK_SET && whence!=SEEK_CUR && whence!=SEEK_END)
		return -EINVAL;
	int newpos=0;
	int temp=f->f_pos;
	
	switch(whence)
	{
		case SEEK_SET:
				temp=offset;
				newpos=temp;
				break;
		case SEEK_END:
				temp=f->f_vnode->vn_len+offset;
				newpos=temp;
				break;
		default:
				temp+=offset;
				newpos=temp;						
	}
	
	if(newpos>=0)
		f->f_pos=newpos;
	else
   		return -EINVAL;
	
        return newpos;
}




/*
 * Find the vnode associated with the path, and call the stat() vnode operation.
 *
 * Error cases you must handle for this function at the VFS level:
 *      o ENOENT
 *        A component of path does not exist.
 *      o ENOTDIR
 *        A component of the path prefix of path is not a directory.
 *      o ENAMETOOLONG
 *        A component of path was too long.
 */
int
do_stat(const char *path, struct stat *buf)
{
       /* NOT_YET_IMPLEMENTED("VFS: do_stat");*/
	
	vnode_t *nvnode;
	vnode_t *altvnode;
	
	if(strcmp(path,"")==0)
		return -EINVAL;
	


	const char *name;
	size_t namelen;
	int retDir=dir_namev(path,&namelen,&name,NULL,&nvnode);
	if(namelen==0 && path[0]=='/')
		name="/";
	if(retDir>=0)
		vput(nvnode);
	if(retDir==-ENOENT)
		return -ENOENT;
	if(strlen(name)>NAME_LEN) 
	{
		return -ENAMETOOLONG;
	}

	
	int lookupResult=lookup(nvnode,name,namelen,&altvnode);
	if(lookupResult>=0)
		vput(altvnode);

	if(lookupResult==-ENOTDIR)	
	{		
		return -ENOTDIR;	
	}
	if(lookupResult==-ENOENT) 
	{
		return -ENOENT;
	}
	
	
	KASSERT(nvnode->vn_ops->stat);
	int statret=nvnode->vn_ops->stat(altvnode, buf);
	
		/*HANLDE 2 ?*/
      return statret;
}
#ifdef __MOUNTING__
/*
 * Implementing this function is not required and strongly discouraged unless
 * you are absolutely sure your Weenix is perfect.
 *
 * This is the syscall entry point into vfs for mounting. You will need to
 * create the fs_t struct and populate its fs_dev and fs_type fields before
 * calling vfs's mountfunc(). mountfunc() will use the fields you populated
 * in order to determine which underlying filesystem's mount function should
 * be run, then it will finish setting up the fs_t struct. At this point you
 * have a fully functioning file system, however it is not mounted on the
 * virtual file system, you will need to call vfs_mount to do this.
 *
 * There are lots of things which can go wrong here. Make sure you have good
 * error handling. Remember the fs_dev and fs_type buffers have limited size
 * so you should not write arbitrary length strings to them.
 */
int
do_mount(const char *source, const char *target, const char *type)
{
        NOT_YET_IMPLEMENTED("MOUNTING: do_mount");
        return -EINVAL;
}

/*
 * Implementing this function is not required and strongly discouraged unless
 * you are absolutley sure your Weenix is perfect.
 *
 * This function delegates all of the real work to vfs_umount. You should not worry
 * about freeing the fs_t struct here, that is done in vfs_umount. All this function
 * does is figure out which file system to pass to vfs_umount and do good error
 * checking.
 */
int
do_umount(const char *target)
{
        NOT_YET_IMPLEMENTED("MOUNTING: do_umount");
        return -EINVAL;
}
#endif
